unit CoreDisplay;

interface

uses
	SysUtils,
{$IFDEF WIN32}
	Windows
{$ENDIF}
{$IFDEF LINUX}
	xlib, xf86vmode
{$ENDIF}
	;

type
	TDisplayMode = record
		w, h, freq: Integer;
	end;
	
	TDisplay = class
	{$IFDEF WIN32}
		realModes: array of TDevMode;
	{$ENDIF}
	{$IFDEF LINUX}
		disp: PDisplay;
		scr: Integer;
		canChangeMode: Boolean;
		realModes: array of PXF86VidModeModeInfo;
	{$ENDIF}
		modes: array of TDisplayMode;
		startMode, curMode: Integer;
		
		procedure Init;
		function SetMode(w, h: Integer): Boolean;
		
		constructor Create;
		destructor Destroy; override;
	end;

var
	disp: TDisplay;

implementation

uses
	CoreConsole;

constructor TDisplay.Create;
begin
end;

destructor TDisplay.Destroy;
begin
{$IFDEF WIN32}
	ChangeDisplaySettings(DevMode(nil^), 0);
{$ENDIF}
{$IFDEF LINUX}
	XF86VidModeSwitchToMode(disp, scr, realModes[startMode]);
	XF86VidModeSetViewPort(disp, scr, modes[startMode].w, modes[startMode].h);
	
	if disp <> nil then XCloseDisplay(disp);
{$ENDIF}
end;

procedure TDisplay.Init;
var
	i: Integer;
{$IFDEF WIN32}
	firstMode, mode: TDevMode;
{$ENDIF}
{$IFDEF LINUX}
	j: Integer;
	firstMode: TXF86VidModeModeLine;
{$ENDIF}
begin
{$IFDEF WIN32}
	EnumDisplaySettings(nil, ENUM_CURRENT_SETTINGS, firstMode);
	
	i := 0;
	
	while EnumDisplaySettings(nil, i, mode) do
	begin
		if mode.dmBitsPerPel <> 32 then
		begin
			i += 1;
			Continue;
		end;
		
		SetLength(realModes, Length(realModes) + 1);
		Move(mode, realModes[High(realModes)], SizeOf(mode));
		
		SetLength(modes, Length(modes) + 1);
		
		with modes[High(modes)] do
		begin
			w := mode.dmPelsWidth;
			h := mode.dmPelsHeight;
		end;
		
		if (mode.dmPelsWidth = firstMode.dmPelsWidth) and (mode.dmPelsHeight = firstMode.dmPelsHeight) and
			 (mode.dmBitsPerPel = firstMode.dmBitsPerPel) and (mode.dmDisplayFrequency = firstMode.dmDisplayFrequency) then
		begin
			startMode := High(modes);
			curMode := startMode;
		end;
		
		i += 1;
	end;
{$ENDIF}
{$IFDEF LINUX}
	disp := XOpenDisplay(nil);

	if disp = nil then
	begin
		//error
		Exit;
	end;
	
	scr := DefaultScreen(disp);
	
	if not Boolean(XF86VidModeQueryExtension(disp, @i, @j)) Then
	begin
		canChangeMode := False;
		Exit;
	end;
	
	canChangeMode := True;
	
	XF86VidModeGetModeLine(disp, scr, @i, @firstMode);
	XF86VidModeGetAllModeLines(disp, scr, @j, @realModes);
	
	SetLength(modes, j);
	
	for i := 0 to j - 1 do
	begin
		with modes[i] do
		begin
			w := realModes[i]^.hdisplay;
			h := realModes[i]^.vdisplay;
		end;
		
		if (modes[i].w = firstMode.hdisplay) and (modes[i].h = firstMode.vdisplay) then
		begin
			startMode := i;
			curMode := startMode;
		end;
	end;
{$ENDIF}
end;

function TDisplay.SetMode(w, h: Integer): Boolean;
var
	i: Integer;
begin
	Result := False;
	
	if w = 0 then w := modes[startMode].w;
	if h = 0 then h := modes[startMode].h;
	
	for i := High(modes) downto 0 do
	begin
		if (modes[i].w = w) and (modes[i].h = h) {$IFDEF WIN32}and (modes[i].freq = modes[startMode].freq){$ENDIF} then
		begin
		{$IFDEF WIN32}
			if ChangeDisplaySettings(@realModes[i], CDS_FULLSCREEN) <> DISP_CHANGE_SUCCESSFUL then
			begin
				con.Msg('#Не удается установить видео-режим %dx%d', [w, h]);
				Continue;
			end;
			
			con.Msg('Видео-режим %dx%d установлен успешно', [w, h]);
			
			Result := True;
			Exit;
		{$ENDIF}
		{$IFDEF LINUX}
			XF86VidModeSwitchToMode(disp, scr, realModes[i]);
			XF86VidModeSetViewPort(disp, scr, 0, 0 );
			
			con.Msg('Видео-режим %dx%d установлен успешно', [w, h]);
		{$ENDIF}
		end;
	end;
end;

end.
